package reports;

import static org.junit.Assert.*;

import java.text.ParseException;
import java.util.Date;

import model.entity.Product;

import org.junit.Test;

import reports.visitor.IVisitor;

import common.Size;
import common.util.DateUtils;

/**
 * Tests the Product Statistics Report based on item actions
 * 
 * @author Paula Chen
 */
public class ProductStatisticsReportItemTest extends ProductStatisticsReportTestSuperclass
{
	
  @Test
  public void testItemsAddedDifferentTime()
  {
    try
    {
      ProductStatisticsReport report;

      final Date today = DateUtils.parseDate("03/31/2013");
      DateUtils.setCurrentDate(today);

      setItemManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          try
          {
            // generate 1 item one day after end of report period
            Date entryDate = DateUtils.parseDate("04/01/2013");
            generateItem(entryDate).preOrderAccept(visitor);  

            // generate 2 items on last day of report period
            for (int i = 0; i < 2; i++)
              generateItem(today).preOrderAccept(visitor);

            // generate 3 items one day before end of report period
            entryDate = DateUtils.parseDate("03/30/2012");
            for (int i = 0; i < 3; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // four month ago

            // generate 4 items four month before end of report period, less one day
            entryDate = DateUtils.parseDate("12/01/2012");
            for (int i = 0; i < 4; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // generate 5 items four month before end of report period
            entryDate = DateUtils.parseDate("11/30/2012");
            for (int i = 0; i < 5; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // generate 6 items four month and one day before end of report period
            entryDate = DateUtils.parseDate("11/29/2012");
            for (int i = 0; i < 6; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // 12 months ago

            // generate 7 items 11 months before end of report period, less one month
            entryDate = DateUtils.parseDate("04/30/2012");
            for (int i = 0; i < 7; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // generate 8 items 12 months before end of report period
            entryDate = DateUtils.parseDate("03/31/2012");
            for (int i = 0; i < 8; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // generate 9 items 13 months before end of report period
            entryDate = DateUtils.parseDate("02/29/2012");
            for (int i = 0; i < 9; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // 100 months ago

            // generate 10 items 100 months before end of report period, less one day
            entryDate = DateUtils.parseDate("10/01/2003");
            for (int i = 0; i < 10; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // generate 11 items 100 months before end of report period
            entryDate = DateUtils.parseDate("09/30/2003");
            for (int i = 0; i < 11; i++)
              generateItem(entryDate).preOrderAccept(visitor);

            // generate 12 items 100 months and one day before end of report period
            entryDate = DateUtils.parseDate("09/29/2003");
            for (int i = 0; i < 12; i++)
              generateItem(entryDate).preOrderAccept(visitor);
          }
          catch (ParseException e)
          {
            fail(e.getMessage());
          }
        }
      });

      setProductManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          getDefaultProduct().preOrderAccept(visitor);
        }
      });

      Product product = getDefaultProduct();

      // //////////////////////////////////////////////////////////////////////////////////////////
      // a. One Month Report (32 days)
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(1);
      int total = 77;
      int added = 2;
      int addedOnFirstDay = 0;
      int minSupply = total - added + addedOnFirstDay;
      int maxAge = 3471;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(75.0625, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(total, report.getMaxSupply(product));

      assertEquals(0, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertTrue(Float.isNaN(report.getAverageUsedAge(product)));
      assertEquals(0, report.getMaxUsedAge(product));

      assertEquals(1639.6883f, report.getAverageCurrentAge(product), 0.1f);
      assertEquals(maxAge, report.getMaxCurrentAge(product));

      // //////////////////////////////////////////////////////////////////////////////////////////
      // a. 12 Month Report (366 days)
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(12);
      added = 32;
      addedOnFirstDay = 8;
      minSupply = total - added + addedOnFirstDay;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(64.437, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(total, report.getMaxSupply(product));

      assertEquals(0, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertTrue(Float.isNaN(report.getAverageUsedAge(product)));
      assertEquals(0, report.getMaxUsedAge(product));

      assertEquals(1639.6883f, report.getAverageCurrentAge(product), 0.1f);
      assertEquals(maxAge, report.getMaxCurrentAge(product));

      // //////////////////////////////////////////////////////////////////////////////////////////
      // c. 100 Month Report (3471 days)
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(12);
      added = 32;
      addedOnFirstDay = 8;
      minSupply = total - added + addedOnFirstDay;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(64.437f, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(total, report.getMaxSupply(product));

      assertEquals(0, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertTrue(Float.isNaN(report.getAverageUsedAge(product)));
      assertEquals(0, report.getMaxUsedAge(product));

      assertEquals(1639.6883f, report.getAverageCurrentAge(product), 0.1f);
      assertEquals(maxAge, report.getMaxCurrentAge(product));
    }
    catch (ParseException e)
    {
      fail(e.getMessage());
    }
  }

  @Test
  public void testItemsUsedDifferentTiem()
  {
    Product product = generateProduct();
    product.setShelfLife(1);
    product.setSize(new Size());
    product.setThreeMonthSupply(1);
    setDefaultProduct(product);

    try
    {
      ProductStatisticsReport report;

      final Date today = DateUtils.parseDate("08/31/2013");
      DateUtils.setCurrentDate(today);

      setItemManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          try
          {
            // use 1 item one day after end of report period
            Date exitDate = DateUtils.parseDate("09/01/2013");
            Date entryDate = DateUtils.parseDate("07/31/2011");
            generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // use 2 items on last day of report period
            for (int i = 0; i < 2; i++)
              generateItem(entryDate,today).preOrderAccept(visitor);

            // use 3 items one day before end of report period
            exitDate = DateUtils.parseDate("08/30/2013");
            for (int i = 0; i < 3; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // four month ago

            // generate 4 items four month before end of report period, less one day
            exitDate = DateUtils.parseDate("05/01/2013");
            for (int i = 0; i < 4; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // use 5 items four month before end of report period
            exitDate = DateUtils.parseDate("04/30/2013");
            for (int i = 0; i < 5; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // use 6 items four month and one day before end of report period
            exitDate = DateUtils.parseDate("04/29/2013");
            for (int i = 0; i < 6; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // 12 months ago

            // use 7 items 11 months before end of report period, less one month
            exitDate = DateUtils.parseDate("09/30/2012");
            for (int i = 0; i < 7; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // use 8 items 12 months before end of report period
            exitDate = DateUtils.parseDate("08/31/2012");
            for (int i = 0; i < 8; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // use 9 items 13 months before end of report period
            exitDate = DateUtils.parseDate("07/31/2012");
            for (int i = 0; i < 9; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // 100 months ago

            entryDate = DateUtils.parseDate("07/31/2001");
            // use 10 items 100 months before end of report period, less one day
            exitDate = DateUtils.parseDate("05/01/2003");
            for (int i = 0; i < 10; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // use 11 items 100 months before end of report period
            exitDate = DateUtils.parseDate("04/30/2003");
            for (int i = 0; i < 11; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  

            // use 12 items 100 months and one day before end of report period
            exitDate = DateUtils.parseDate("04/29/2003");
            for (int i = 0; i < 12; i++)
                generateItem(entryDate,exitDate).preOrderAccept(visitor);  
          }
          catch (ParseException e)
          {
            fail(e.getMessage());
          }
        }
      });

      setProductManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          getDefaultProduct().preOrderAccept(visitor);
        }
      });

      // //////////////////////////////////////////////////////////////////////////////////////////
      // a. 4 Month Report (124 days)
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(4);
      int total = 1;
      int added = 0;
      int addedOnFirstDay = 0;
      int minSupply = total - added + addedOnFirstDay;
      int maxAge = 763;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(5.9677f, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(10, report.getMaxSupply(product));

      assertEquals(14, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertEquals(683,report.getAverageUsedAge(product), 0.1f);
      assertEquals(762, report.getMaxUsedAge(product));

      assertEquals(763, report.getAverageCurrentAge(product), 0.1f);
      assertEquals(maxAge, report.getMaxCurrentAge(product));

      // //////////////////////////////////////////////////////////////////////////////////////////
      // b. 11 Month Report (336 days)
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(11);
      added = 0;
      addedOnFirstDay = 0;
      minSupply = total - added + addedOnFirstDay;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(15.4345f, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(21, report.getMaxSupply(product));

      assertEquals(27, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertEquals(606.6298f, report.getAverageUsedAge(product), 0.1f);
      assertEquals(762, report.getMaxUsedAge(product));

      assertEquals(763, report.getAverageCurrentAge(product), 0.1f);
      assertEquals(maxAge, report.getMaxCurrentAge(product));

      // //////////////////////////////////////////////////////////////////////////////////////////
      // c. 99 Month Report (3746 days)
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(100);
      total = 1;
      added = 45;
      addedOnFirstDay = 44;
      minSupply = total - added + addedOnFirstDay;
      maxAge = 763;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(7.7518f, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(45, report.getMaxSupply(product));

      assertEquals(44, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertEquals(519.2954f, report.getAverageUsedAge(product), 0.1f);
      assertEquals(762, report.getMaxUsedAge(product));

      assertEquals(763, report.getAverageCurrentAge(product), 0.1f);
      assertEquals(maxAge, report.getMaxCurrentAge(product));
    }
    catch (ParseException e)
    {
      fail(e.getMessage());
    }
  }
  
  @Test
  public void testNoItemChanged()
  {
    Product product = generateProduct();
    product.setShelfLife(1);
    product.setSize(new Size());
    product.setThreeMonthSupply(1);
    setDefaultProduct(product);

    try
    {
      ProductStatisticsReport report;

      final Date today = DateUtils.parseDate("05/08/2014");
      DateUtils.setCurrentDate(today);

      setItemManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          try
          {
            // generate 1000 item one day after end of report period
            Date entryDate = DateUtils.parseDate("09/01/2012");
            Date exitDate = DateUtils.parseDate("09/01/2013");
            for (int i = 0; i < 1000; i++)
            	generateItem(entryDate, exitDate).preOrderAccept(visitor);  

          }
          catch (ParseException e)
          {
            fail(e.getMessage());
          }
        }
      });

      setProductManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          getDefaultProduct().preOrderAccept(visitor);
        }
      });

      // //////////////////////////////////////////////////////////////////////////////////////////
      // a. Huge amount added before the report period. None used.
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(12);
      int total = 0;
      int added = 0;
      int addedOnFirstDay = 0;
      int minSupply = total - added + addedOnFirstDay;
      int maxAge = 0;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(316.9398f, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(1000, report.getMaxSupply(product));

      assertEquals(1000, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertEquals(365,report.getAverageUsedAge(product), 0.1f);
      assertEquals(365, report.getMaxUsedAge(product));

      assertTrue(Float.isNaN(report.getAverageCurrentAge(product)));
      assertEquals(maxAge, report.getMaxCurrentAge(product));

      // //////////////////////////////////////////////////////////////////////////////////////////
      // b. Huge amount added before the report period. None left.
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(1);
      added = 0;
      addedOnFirstDay = 0;
      minSupply = total - added + addedOnFirstDay;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(0, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(0, report.getMaxSupply(product));

      assertEquals(0, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertTrue(Float.isNaN(report.getAverageUsedAge(product)));
      assertEquals(0, report.getMaxUsedAge(product));

      assertTrue(Float.isNaN(report.getAverageCurrentAge(product)));
      assertEquals(maxAge, report.getMaxCurrentAge(product));

      // //////////////////////////////////////////////////////////////////////////////////////////
      // c. Nothing added.
      // //////////////////////////////////////////////////////////////////////////////////////////

      ProductStatisticsReport report2;

      final Date today2 = DateUtils.parseDate("05/08/2014");
      DateUtils.setCurrentDate(today2);

      setProductManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          getDefaultProduct().preOrderAccept(visitor);
        }
      });
        
        
      report2 = generateReport(5);
      total = 0;
      added = 0;
      addedOnFirstDay = 0;
      minSupply = total - added + addedOnFirstDay;
      maxAge = 0;

      assertEquals(total, report2.getCurrentSupply(product));
      assertEquals(0, report2.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report2.getMinSupply(product));
      assertEquals(0, report2.getMaxSupply(product));

      assertEquals(0, report2.getQuantityUsed(product));
      assertEquals(added, report2.getQuantityAdded(product));

      assertTrue(Float.isNaN(report.getAverageUsedAge(product)));
      assertEquals(0, report2.getMaxUsedAge(product));

      assertTrue(Float.isNaN(report.getAverageCurrentAge(product)));
      assertEquals(maxAge, report2.getMaxCurrentAge(product));
    }
    catch (ParseException e)
    {
      fail(e.getMessage());
    }
  }
  
  @Test
  public void testItemsChangedOnTheSameDay()
  {
    Product product = generateProduct();
    product.setShelfLife(1);
    product.setSize(new Size());
    product.setThreeMonthSupply(1);
    setDefaultProduct(product);

    try
    {
      ProductStatisticsReport report;

      Date today = DateUtils.parseDate("11/13/2013");
      DateUtils.setCurrentDate(today);

      setItemManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          try
          {
            // generate 10 item one day after end of report period
            Date entryDate = DateUtils.parseDate("09/13/2013");
            Date exitDate = DateUtils.parseDate("09/13/2013");
            for (int i = 0; i < 10; i++)
            	generateItem(entryDate, exitDate).preOrderAccept(visitor);  

          }
          catch (ParseException e)
          {
            fail(e.getMessage());
          }
        }
      });

      setProductManagerVisitorNode(new MockNode()
      {
        @Override
        public void preOrderAccept(IVisitor visitor)
        {
          getDefaultProduct().preOrderAccept(visitor);
        }
      });

      // //////////////////////////////////////////////////////////////////////////////////////////
      // a. Items added and used on the same day before the report period
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(1);
      int total = 0;
      int added = 0;
      int addedOnFirstDay = 0;
      int minSupply = total - added + addedOnFirstDay;
      int maxAge = 0;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(0, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(0, report.getMaxSupply(product));

      assertEquals(0, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertTrue(Float.isNaN(report.getAverageUsedAge(product)));
      assertEquals(0, report.getMaxUsedAge(product));

      assertTrue(Float.isNaN(report.getAverageCurrentAge(product)));
      assertEquals(maxAge, report.getMaxCurrentAge(product));
      
      // //////////////////////////////////////////////////////////////////////////////////////////
      // b. Items added and used on the same day in the report period
      // //////////////////////////////////////////////////////////////////////////////////////////

      report = generateReport(2);

      added = 10;
      addedOnFirstDay = 10;
      minSupply = total - added + addedOnFirstDay;
      maxAge = 0;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(0, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(0, report.getMaxSupply(product));

      assertEquals(10, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertEquals(0,report.getAverageUsedAge(product), 0.1f);
      assertEquals(0, report.getMaxUsedAge(product));

      assertTrue(Float.isNaN(report.getAverageCurrentAge(product)));
      assertEquals(maxAge, report.getMaxCurrentAge(product));

      // //////////////////////////////////////////////////////////////////////////////////////////
      // b.Items added and used and run on the same day
      // //////////////////////////////////////////////////////////////////////////////////////////

      today = DateUtils.parseDate("09/13/2013");
      DateUtils.setCurrentDate(today);
      
      report = generateReport(1);
      added = 10;
      addedOnFirstDay = 10;
      minSupply = total - added + addedOnFirstDay;

      assertEquals(total, report.getCurrentSupply(product));
      assertEquals(0, report.getAverageSupply(product), 0.1f);

      assertEquals(minSupply, report.getMinSupply(product));
      assertEquals(0, report.getMaxSupply(product));

      assertEquals(10, report.getQuantityUsed(product));
      assertEquals(added, report.getQuantityAdded(product));

      assertEquals(0, report.getAverageUsedAge(product), 0.1f);
      assertEquals(0, report.getMaxUsedAge(product));

      assertTrue(Float.isNaN(report.getAverageCurrentAge(product)));
      assertEquals(maxAge, report.getMaxCurrentAge(product));
      
    }
    catch (ParseException e)
    {
      fail(e.getMessage());
    }
  }
}